package com.taobao.tddl.matrix.jdbc;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Statement;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import com.taobao.tddl.common.exception.TddlRuntimeException;
import com.taobao.tddl.common.jdbc.ParameterContext;
import com.taobao.tddl.common.model.ExtraCmd;
import com.taobao.tddl.common.model.SqlType;
import com.taobao.tddl.common.utils.GeneralUtil;
import com.taobao.tddl.executor.common.ExecutionContext;
import com.taobao.tddl.matrix.jdbc.utils.PreParser;

import com.taobao.tddl.common.utils.logger.Logger;
import com.taobao.tddl.common.utils.logger.LoggerFactory;

/**
 * @author mengshi.sunmengshi 2013-11-22 下午3:26:28
 * @since 5.0.0
 */
public class TStatement implements Statement {

    private static final Logger   log                  = LoggerFactory.getLogger(TStatement.class);

    protected String              sql;
    protected TDataSource         ds;
    protected TConnection         conn;

    protected ExecutionContext    executionContext     = null;
    /**
     * 更新计数，如果执行了多次，那么这个值只会返回最后一次执行的结果。 如果是一个query，那么返回的数据应该是-1
     */
    protected int                 updateCount;

    /**
     * 经过计算后的结果集，允许使用 getResult函数调用. 一个statement只允许有一个结果集
     */
    protected ResultSet           currentResultSet;

    protected Map<String, Object> extraCmd             = new HashMap<String, Object>(4);

    /**
     * 当前statment 是否是关闭的
     */
    protected boolean             closed;

    private int                   maxFieldSize;

    private int                   maxRows;

    private int                   queryTimeOut;

    private int                   direction;

    protected List<String>        batchedArgs;

    private int                   resultSetType        = -1;

    private int                   resultSetConcurrency = -1;

    private int                   resultSetHoldability = -1;

    public TStatement(TDataSource ds, TConnection tConnection, ExecutionContext executionContext){
        this.ds = ds;
        this.conn = tConnection;
        this.executionContext = executionContext;
    }

    public TStatement(TDataSource ds, TConnection tConnection, String sql, ExecutionContext executionContext){
        this.ds = ds;
        this.conn = tConnection;
        this.sql = sql;
        this.executionContext = executionContext;
    }

    protected void checkClosed() throws SQLException {
        if (closed) {
            throw new SQLException("No operations allowed after statement closed.");
        }
    }

    // jdbc规范: 返回true表示executeQuery，false表示executeUpdate
    public boolean execute(String sql) throws SQLException {
        return executeInternal(sql, -1, null, null);
    }

    public boolean execute(String sql, int autoGeneratedKeys) throws SQLException {
        throw new UnsupportedOperationException();
    }

    public boolean execute(String sql, int[] columnIndexes) throws SQLException {
        throw new UnsupportedOperationException();
    }

    public boolean execute(String sql, String[] columnNames) throws SQLException {
        throw new UnsupportedOperationException();
    }

    private boolean executeInternal(String sql, int autoGeneratedKeys, int[] columnIndexes, String[] columnNames)
                                                                                                                 throws SQLException {

        SqlType sqlType = PreParser.getSqlType(sql);
        if (sqlType == SqlType.SELECT || sqlType == SqlType.SELECT_FOR_UPDATE/**
         * 
         * || sqlType == SqlType.SHOW
         **/
        ) {
            executeQuery(sql);
            return true;
        } else if (sqlType == SqlType.INSERT || sqlType == SqlType.UPDATE || sqlType == SqlType.DELETE
                   || sqlType == SqlType.REPLACE || sqlType == SqlType.TRUNCATE || sqlType == SqlType.CREATE
                   || sqlType == SqlType.DROP || sqlType == SqlType.LOAD || sqlType == SqlType.MERGE) {
            if (autoGeneratedKeys == -1 && columnIndexes == null && columnNames == null) {
                executeUpdate(sql);
            } else if (autoGeneratedKeys != -1) {
                executeUpdate(sql, autoGeneratedKeys);
            } else if (columnIndexes != null) {
                executeUpdate(sql, columnIndexes);
            } else if (columnNames != null) {
                executeUpdate(sql, columnNames);
            } else {
                executeUpdate(sql);
            }

            return false;
        } else {
            throw new SQLException("only select, insert, update, delete,replace,truncate,create,drop,load,merge sql is supported");
        }
    }

    /*
     * ========================================================================
     * executeQuery 查询逻辑 这里按照mysql
     * connection逻辑，调用connection的executeSQL方法返回resultset Connection 的 execSQL方法
     * ======================================================================
     */
    public ResultSet executeQuery(String sql) throws SQLException {
        checkClosed();
        ensureResultSetIsEmpty();
        currentResultSet = this.conn.executeSQL(sql,
            (Map<Integer, ParameterContext>) Collections.EMPTY_MAP,
            this,
            extraCmd,
            this.executionContext);
        return currentResultSet;
    }

    /*
     * ========================================================================
     * executeUpdate逻辑 这里按照mysql
     * connection逻辑，调用connection的executeSQL方法返回resultset
     * ,然后根据resultset获得affertrows Connection 的 execSQL方法
     * ======================================================================
     */
    public int executeUpdate(String sql) throws SQLException {
        checkClosed();
        ensureResultSetIsEmpty();
        currentResultSet = this.conn.executeSQL(sql,
            ((Map<Integer, ParameterContext>) Collections.EMPTY_MAP),
            this,
            extraCmd,
            this.executionContext);
        return ((TResultSet) currentResultSet).getAffectRows();
    }

    public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException {
        throw new UnsupportedOperationException();
    }

    public int executeUpdate(String sql, int[] columnIndexes) throws SQLException {
        throw new UnsupportedOperationException();
    }

    public int executeUpdate(String sql, String[] columnNames) throws SQLException {
        throw new UnsupportedOperationException();
    }

    public void addBatch(String sql) throws SQLException {
        checkClosed();
        if (batchedArgs == null) {
            batchedArgs = new LinkedList<String>();
        }
        if (sql != null) {
            batchedArgs.add(sql);
        }
    }

    public void clearBatch() throws SQLException {
        checkClosed();
        if (batchedArgs != null) {
            batchedArgs.clear();
        }
    }

    public int[] executeBatch() throws SQLException {
        throw new UnsupportedOperationException("executeBatch");
    }

    public boolean isClosed() throws SQLException {
        return this.closed;
    }

    public void close() throws SQLException {
        close(true);
    }

    void close(boolean removeThis) throws SQLException {
        if (closed) {
            return;
        }
        try {
            if (currentResultSet != null) {
                currentResultSet.close();
            }

            if (removeThis) {
                conn.removeStatement(this);
            }
        } catch (Exception e) {
            throw new TddlRuntimeException(e);
        } finally {
            currentResultSet = null;
        }
        closed = true;
    }

    /**
     * 如果新建了查询，那么上一次查询的结果集应该被显示的关闭掉。这才是符合jdbc规范的
     * 
     * @throws SQLException
     */
    protected void ensureResultSetIsEmpty() throws SQLException {
        if (currentResultSet != null) {
            // log.debug("result set is not null,close current result set");
            try {
                currentResultSet.close();
            } catch (SQLException e) {
                log.error("exception on close last result set . can do nothing..", e);
            } finally {
                // 最终要显示的关闭它
                currentResultSet = null;
            }
        }

    }

    public int getMaxFieldSize() throws SQLException {
        return this.maxFieldSize;
    }

    public void setMaxFieldSize(int max) throws SQLException {
        this.maxFieldSize = max;

    }

    public int getMaxRows() throws SQLException {
        return this.maxRows;
    }

    public void setMaxRows(int max) throws SQLException {
        this.maxRows = max;
    }

    public ResultSet getResultSet() throws SQLException {
        return currentResultSet;
    }

    public int getUpdateCount() throws SQLException {
        return updateCount;
    }

    public Connection getConnection() throws SQLException {
        return this.conn;
    }

    public int getQueryTimeout() throws SQLException {
        return this.queryTimeOut;
    }

    public void setQueryTimeout(int seconds) throws SQLException {
        this.queryTimeOut = seconds;

    }

    public <T> T unwrap(Class<T> iface) throws SQLException {
        try {
            return (T) this;
        } catch (Exception e) {
            throw new SQLException(e);
        }
    }

    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        return this.getClass().isAssignableFrom(iface);
    }

    public void setFetchDirection(int direction) throws SQLException {
        this.direction = direction;

    }

    public int getFetchDirection() throws SQLException {
        return this.direction;
    }

    public void setFetchSize(int rows) throws SQLException {
        extraCmd.put(ExtraCmd.FETCH_SIZE, rows);
    }

    public int getFetchSize() throws SQLException {
        return (int) GeneralUtil.getExtraCmdLong(extraCmd, ExtraCmd.FETCH_SIZE, 0L);
    }

    public int getResultSetConcurrency() throws SQLException {
        return resultSetConcurrency;
    }

    public int getResultSetType() throws SQLException {
        return resultSetType;
    }

    public int getResultSetHoldability() throws SQLException {
        return resultSetHoldability;
    }

    public SQLWarning getWarnings() throws SQLException {
        return null;
    }

    public void clearWarnings() throws SQLException {
    }

    public void setResultSetType(int resultSetType) {
        this.resultSetType = resultSetType;
    }

    public void setResultSetConcurrency(int resultSetConcurrency) {
        this.resultSetConcurrency = resultSetConcurrency;
    }

    public void setResultSetHoldability(int resultSetHoldability) {
        this.resultSetHoldability = resultSetHoldability;
    }

    /*---------------------后面是未实现的方法------------------------------*/

    public void setPoolable(boolean poolable) throws SQLException {
        throw new SQLException("not support exception");
    }

    public boolean isPoolable() throws SQLException {
        throw new SQLException("not support exception");
    }

    public void setEscapeProcessing(boolean enable) throws SQLException {
        throw new UnsupportedOperationException("setEscapeProcessing");
    }

    public void setCursorName(String cursorName) throws SQLException {
        throw new UnsupportedOperationException("setCursorName");
    }

    public boolean getMoreResults() throws SQLException {
        throw new UnsupportedOperationException("getMoreResults");
    }

    public boolean getMoreResults(int current) throws SQLException {
        throw new UnsupportedOperationException("getMoreResults");
    }

    public ResultSet getGeneratedKeys() throws SQLException {
        throw new UnsupportedOperationException("getGeneratedKeys");
    }

    public void cancel() throws SQLException {
        throw new UnsupportedOperationException("cancel");
    }
}
